/*
 * Project: Sudoku Explainer
 * Copyright (C) 2006-2007 Nicolas Juillerat
 * 
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver;

import java.util.BitSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import com.onegravity.sudoku.solver.checks.MissingPencilMarksHint;
import com.onegravity.sudoku.solver.core.DirectHint;
import com.onegravity.sudoku.solver.core.Hint;
import com.onegravity.sudoku.solver.core.IndirectHint;
import com.onegravity.sudoku.solver.core.WarningHint;
import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.data.ImmutableCell;

/**
 * Class to filter hints with similar outcome
 */
class HintFilter {
	
	private Set<Cell> cells2Set;
	private Map<ImmutableCell, BitSet> pencilMarks2Remove;

	HintFilter() {
	    cells2Set = new HashSet<Cell>();
	    pencilMarks2Remove = new TreeMap<ImmutableCell, BitSet>();
	}

	void reset() {
		cells2Set.clear();
		pencilMarks2Remove.clear();
	}

	boolean isEligible(Hint hint) {
		if (hint instanceof DirectHint) {
			return isEligibleCell(hint);
		}
		else if (hint instanceof WarningHint || hint instanceof MissingPencilMarksHint) {
			return true;
		}
		else if (hint instanceof IndirectHint) {
		    Map<Cell,BitSet> removablePencilMarks = ((IndirectHint) hint).getRemovablePotentials();
		    if (removablePencilMarks!=null) {
		    	boolean isEligible = false;
		    	for (Cell cell : removablePencilMarks.keySet()) {
		    		ImmutableCell immutableCell = new ImmutableCell(cell);
		    		
		    		// get the hint's removable pencil marks
		    		BitSet newPMs = removablePencilMarks.get(cell);
		    		if (newPMs==null || newPMs.cardinality()==0) continue;
		    		
			    	// retrieve the cell's removable pencil marks (from previous hints)
		    		BitSet currentPMs = pencilMarks2Remove.get(immutableCell);
		    		if (currentPMs==null) currentPMs = new BitSet(); 
		    		int oldCard = currentPMs.cardinality();
		    		currentPMs.or(newPMs);
		    		if (currentPMs.cardinality()!=oldCard) {
		    			isEligible = true;
		    			pencilMarks2Remove.put(immutableCell, currentPMs);
		    		}
		    	}
		    	if (isEligible) return true;
		    }
			return isEligibleCell(hint);
		}
		return false;
	}
	
	private boolean isEligibleCell(Hint hint) {
		Cell cell = hint.getCell(HintType.HINT);
		if (cell==null) return false;
		if (cells2Set.contains(cell)) return false;
		cells2Set.add(cell);
		return true;
	}
}